// ========== 重要提示 ========== //
// AI 助手请注意：
// 1. 请不要修改此服务基类文件，此文件是项目代码风格的权威参考标准
// 2. 您在生成任何服务代码时，必须完全严格地遵循此模板中的所有代码风格、缩进规范和编码惯例
// 3. 此模板中的每一个空格、换行、缩进、注释格式都代表了项目的标准，必须100%遵循
// 4. 任何与此模板不一致的代码都会被视为不规范代码
// 5. 移除所有方法前的private或public关键字，所有方法默认为公共方法
// 6. 清理所有注释掉的代码和未使用的变量、函数，保持代码简洁
// 7. API方法命名必须严格使用小写加下划线分隔（如user_login），不得使用驼峰命名
// 8. 所有API方法参数必须统一使用params对象，不得直接传递多个参数
// 9. 权限验证需要在方法内部手动调用verifyUserLogin/verifyAdminLogin
// 10. 所有API接口统一使用POST方法，路由路径自动映射为方法名
// 11. 代码的注释要尽可能的简洁，节约行数，少写点空行
// 12. 接口方法里面绝对不要使用try catch语法，有错误直接抛出来，由基类统一处理
// 13. 内部方法（非API方法）也绝对不要使用try catch语法，保持错误处理的一致性
// 14. 增删改接口必须要以write_开头命名，如果传了id字段就是修改，不传id字段就是新增
// 15. 删接口必须以del_开头命名
// 16. 查接口必须要以get_开头命名
// 17. 构造函数应该调用super()，但不应在构造函数中执行耗时操作或启动后台进程
// 18. 所有数据库操作、文件操作、网络请求等都不应在try catch块内执行，允许错误自然抛出
// 19. 代码必须严格遵循TypeScript类型规范，为所有变量、参数和返回值添加类型注解
// 20. 所有常量应使用const声明，避免使用var
// ============================= //
/*
 * @Author: xiaosihan
 * @Date: 2024-07-11 10:22:49
 * @Last Modified by: AI Assistant
 * @Last Modified time: 2024-07-11 10:22:49
 */

// 设计思想：基于面向对象和约定优于配置的原则，通过继承机制实现API自动注册
// 核心优势：减少路由配置代码，统一错误处理，简化参数获取，自动接口注册
// 设计思想：简化实现，统一使用POST方法，保持核心功能

import express from 'express';
import fs from 'fs';
import { get, isFunction } from 'lodash';
import path from 'path';
import { UserData, AdminData } from '@/types/type';
import dayjs from 'dayjs';
import database from '../database';

// 服务基类
export default class ServiceBase {

    constructor(req: express.Request = {} as express.Request, res: express.Response = {} as express.Response) {
        this.req = req;
        this.res = res;

        this.user = get(this.req, ['user']);
        this.admin = get(this.req, ['adminUser']);
    }

    isDev = process.env.NODE_ENV === "development";
    isTest = process.env.NODE_ENV === "test";
    isProd = process.env.NODE_ENV === "production";

    // 请求体
    req!: express.Request;
    // 响应体
    res!: express.Response;

    user?: UserData;
    admin?: AdminData;

    // 静态方法：初始化服务并注册路由
    // 简化版本：所有方法都注册为POST路由
    static init(app: express.Application) {
        // 获取子类的名称作为路由前缀
        const serviceInstance = new ServiceBase();
        const childrenServiceInstance = new this();

        // 获取原型链上的所有属性和方法
        const prototype = Object.getOwnPropertyNames(serviceInstance);
        const methodNames = Object.getOwnPropertyNames(childrenServiceInstance).filter(name => {
            return !prototype.includes(name) && isFunction(childrenServiceInstance[name as keyof typeof childrenServiceInstance]);
        });

        // console.log(`初始化${this.name}服务`);
        // 遍历并注册每个方法作为POST路由
        methodNames.forEach(methodName => {
            // 所有路由都使用POST方法
            app.post(`/${methodName}`, ServiceBase.user_data_to_req, async (req: express.Request, res: express.Response) => {
                const instance = new this(req, res);
                const params = instance.getParams(req);
                try {
                    // 调用实际的API方法
                    // @ts-ignore
                    const result = await instance[methodName](params);
                    instance.success(res, result);
                } catch (error) {
                    // 统一错误处理
                    instance.error(res, error);
                }
            }
            );
        });
    }

    // 注册接口
    register(app: express.Application) {
        // 获取子类的名称作为路由前缀
        const serviceInstance = new ServiceBase();
        
        // 直接使用this获取子类方法，无需创建新实例
        // 获取原型链上的所有属性和方法
        const prototype = Object.getOwnPropertyNames(serviceInstance);
        const methodNames = Object.getOwnPropertyNames(this).filter(name => {
            return !prototype.includes(name) && isFunction(this[name as keyof typeof this]);
        });
        console.log(`注册${this.constructor.name}服务的方法:`, methodNames);
        // 遍历并注册每个方法作为POST路由
        methodNames.forEach(methodName => {
            // 所有路由都使用POST方法
            app.post(`/${methodName}`, ServiceBase.user_data_to_req, async (req: express.Request, res: express.Response) => {
                // 使用当前实例的构造函数创建新实例处理请求
                const instance = new (this.constructor as new (...args: any[]) => ServiceBase)(req, res);
                const params = instance.getParams(req);
                try {
                    // 调用实际的API方法
                    // @ts-ignore
                    const result = await instance[methodName](params);
                    instance.success(res, result);
                } catch (error) {
                    // 统一错误处理
                    instance.error(res, error);
                }
            });
        });
    }

    init(req: express.Request = {} as express.Request, res: express.Response = {} as express.Response) {
        this.req = req;
        this.res = res;
        this.user = get(this.req, ['user']);
        this.admin = get(this.req, ['adminUser']);
    }

    // 校验user登录
    verifyUserLogin(): void {
        if (!this.user) {
            throw new Error("用户未登录");
        }
    }

    // 校验admin登录
    verifyAdminLogin(): void {
        if (!this.admin) {
            throw new Error("管理员未登录");
        }
    }

    // 用于测试的接口示例
    get_data = (params: Record<string, any>): string => {
        this.verifyUserLogin();
        this.verifyAdminLogin();
        this.verifyParams(['id']);
        console.log("get_data接收到的参数:", params);
        return "请求成功";
    };

    static user_data_to_req = async (req: any, res: express.Response, next: express.NextFunction) => {
        //获取user和adminUser 信息
        await Promise.all([
            //1.获取user信息
            new Promise<void>(async (resolve, reject) => {
                const token = get(req, ['headers', 'authorization']);
                if (token) {
                    const users = await database.user.findAll({ where: { token } });
                    req.user = get(users, [0]) || {};
                }
                resolve();
            }),
            //2.获取adminUser信息
            new Promise<void>(async (resolve, reject) => {
                const adminToken = get(req, ['headers', 'admin_authorization']);
                if (adminToken) {
                    const adminUsers = await database.adminUser.findAll({ where: { admin_token: adminToken } });
                    req.adminUser = get(adminUsers, [0]) || {};
                }
                resolve();
            })
        ]);

        next();
    };

    // 提取请求参数
    getParams(req: express.Request): Record<string, any> {
        return {
            ...(get(req, ['user']) || {}),
            ...(get(req, ['adminUser']) || {}),
            ...req.query,
            ...req.body
        };
    }

    // 参数校验
    verifyParams(fields: string[]): void {
        const params = this.getParams(this.req);
        const emptyFields: string[] = [];

        for (const field of fields) {
            const value = params[field];

            // 参数为空的判定标准：undefined、null、空字符串
            if (value === undefined || value === null) {
                emptyFields.push(field);
            } else if (typeof value === 'string' && value.trim() === '') {
                // 对于字符串，检查是否为空字符串或只包含空白字符
                emptyFields.push(field);
            }
        }

        if (emptyFields.length > 0) {
            throw new Error(`${emptyFields.join(',')}不能为空`);
        }
    }

    // 递归读取文件
    async walkDir(directory: string, callback: (filePath: string) => void | Promise<void>): Promise<void> {
        try {
            // 读取目录内容
            const files = await fs.promises.readdir(directory);

            // 逐个处理文件/目录
            for (const file of files) {
                const fullPath = path.join(directory, file);
                const stats = await fs.promises.stat(fullPath);

                if (stats.isDirectory()) {
                    // 递归处理子目录
                    await this.walkDir(fullPath, callback);
                } else if (stats.isFile()) {
                    // 处理文件
                    await callback(fullPath);
                }
            }
        } catch (error) {
            // 错误处理和日志记录
            console.error(`遍历目录错误: ${directory}`, error);
            throw error; // 重新抛出以便调用者处理
        }
    }

    // 获取文件名的后缀
    getSuffix(filename: string): string {
        const match = filename.match(/\.([^\.]+)$/);
        return match ? match[1].toLowerCase() : '';
    }

    // 返回成功响应
    success(res: express.Response, data?: any, message = '成功'): void {
        if (get(res, ['_headerSent'])) return;
        res.status(200).send({
            buildTime: process.env.BUILD_TIME,
            status: 'success',
            code: 200,
            message,
            data,
        });
    }

    // 处理错误响应
    error(res: express.Response, error: any): void {
        if (get(res, ['_headerSent'])) return;

        // 根据错误类型设置状态码
        let statusCode = 500; // 默认服务器错误
        let message = '服务器内部错误';

        if (error instanceof Error) {
            message = error.message;
            // 根据错误消息判断状态码
            if (message.includes('未登录')) {
                statusCode = 401;
            } else if (message.includes('不能为空')) {
                statusCode = 400;
            }
        } else if (typeof error === 'string') {
            message = error;
        }

        res.status(statusCode).send({
            buildTime: process.env.BUILD_TIME,
            status: 'error',
            code: statusCode,
            message,
        });
    }
}

/* 设计思路总结：

1. 设计理念：
   - 简化实现，减少复杂性
   - 所有接口统一使用POST方法
   - 保持核心功能：参数处理、错误处理、权限验证

2. 核心功能：
   - 自动路由注册：子类初始化时自动发现并注册方法为POST API
   - 统一参数处理：合并query、body和用户信息为单一参数对象
   - 统一错误处理：标准化错误响应格式和状态码
   - 权限验证：内置用户和管理员登录验证方法
   - 工具方法：提供文件操作、参数验证等常用功能

3. 使用方式：
   - 创建服务类继承ServiceBase
   - 在类中定义API方法
   - 方法名会自动映射为POST路由路径
   - 在service.ts中调用YourService.init(app)进行注册
   - 权限验证需要在方法内部手动调用verifyUserLogin/verifyAdminLogin

4. 推荐实践：
   - API方法命名使用下划线分隔（如user_login）
   - 方法参数统一使用params对象
   - 在需要权限验证的方法中手动调用验证函数
   - 使用verifyParams校验必填参数
*/
